﻿using System;
using System.Collections.Generic;
using NUnit.Framework;
using System.Threading;

namespace ebc.minibus.tests
{
    [TestFixture]
    public class test_EbcMiniBus
    {
        [Test]
        public void Register_input_handler_action()
        {
            var sut = new EbcMiniBus();

            var result = false;
            sut.Register<bool>(m => result = m);

            sut.Send(true);

            Assert.IsTrue(result);
        }


        [Test]
        public void Register_input_handler_function()
        {
            var sut = new EbcMiniBus();

            int result = 0;
            sut.Register<int>(d => result = d);

            sut.Register<string, int>(s => s.Length);

            sut.Send("hello");

            Assert.AreEqual(5, result);
        }


        [Test]
        public void Register_several_input_handlers_for_same_message_type()
        {
            var sut = new EbcMiniBus();

            var results = new List<int>();
            sut.Register<int>(results.Add);
            sut.Register<int>(d => results.Add(d + 1));

            sut.Send(1);

            Assert.AreEqual(new[]{1,2}, results.ToArray());
        }


        [Test]
        public void Register_named_input_handler()
        {
            var sut = new EbcMiniBus();

            var result = false;
            sut.Register<bool>(m => result = m, "myhandler");

            sut.Send(true, "myhandler");

            Assert.IsTrue(result);
        }


        [Test]
        public void Register_named_input_handler_and_send_differently_named_msg()
        {
            var sut = new EbcMiniBus();

            var result = false;
            sut.Register<bool>(m => result = m, "myhandler");

            sut.Send(true, "missing");

            Assert.IsFalse(result);
        }

        
        [Test]
        public void Register_input_handler_function_with_input_and_output_name()
        {
            var sut = new EbcMiniBus();

            var result = 0;
            sut.Register<int>(d => result = d, "result");

            sut.Register<string, int>(s => s.Length, "input", "result");

            sut.Send("hello", "input");

            Assert.AreEqual(5, result);
        }


        [Test]
        public void Post_message_asynchronously()
        {
            var sut = new EbcMiniBus();

            var are = new AutoResetEvent(false);
            var result = "";
            long handlerThreadHashcode = 0;
            sut.Register<string>(s => { 
                result = s;
                handlerThreadHashcode = Thread.CurrentThread.GetHashCode();
                are.Set(); });

            sut.Post("hello");

            Assert.IsTrue(are.WaitOne(1000), "No msg received in background");
            Assert.AreEqual("hello", result);
            Assert.AreNotEqual(Thread.CurrentThread.GetHashCode(), handlerThreadHashcode);
        }


        [Test]
        public void Send_message_to_several_differently_named_handlers()
        {
            var sut = new EbcMiniBus();

            var results = new List<int>();
            sut.Register<int>(results.Add, "int0");
            sut.Register<int>(d => results.Add(d + 1), "int1");

            sut.Send(1, "int0", "int1");

            Assert.AreEqual(new[] { 1, 2 }, results.ToArray());
        }


        [Test]
        public void Handle_background_exception()
        {
            var sut = new EbcMiniBus();

            var are = new AutoResetEvent(false);
            Exception result = null;
            sut.Register<bool>(b => {throw new ArgumentException();});
            sut.OnBackgroundException += ex => { result = ex; are.Set(); };

            sut.Post(true);

            Assert.IsTrue(are.WaitOne(500), "No execption received from background");
            Assert.IsInstanceOf(typeof(ArgumentException), result);
        }


        [Test]
        public void Unregister_message_handler()
        {
            var sut = new EbcMiniBus();

            var result = "";
            var registration = sut.Register<string>(_ => result = _);

            sut.Send("hello");
            Assert.AreEqual("hello", result);

            registration.Dispose();
            sut.Send("world");
            Assert.AreEqual("hello", result);
        }
    }
}
